import re
from multiprocessing import Process
from multiprocessing import Queue
from multiprocessing.queues import Empty
from multiprocessing.queues import Full

import requests as r

INVALID_FILENAME_CHARS = re.compile(r"[\\/:*?\"'<>|]")

def cookie_str_to_dict(cookie: str) -> dict:
    raw_cookie = cookie.strip()
    if raw_cookie == "":
        return dict()
    else:
        return dict(map(lambda kv: kv.split("="),
                        cookie.strip().split("; ")))

def ensure_filename(filename: str, replace="") -> str:
    """返回符合文件名规定的新字符串
    不合法的字符将替换为 replace 所指定的字符

    :param filename: 未确认的文件名
    :param replace: 用于替换非法字符的字符，默认空
    """
    return re.sub(INVALID_FILENAME_CHARS, repl=replace, string=filename)

class SimpleHttpDownloader(Process):
    """基于多进程的 HTTP 下载器

    初始化传入获取 url 的队列 urls，
    当一次请求完毕后将 bytes 类型的响应推入输出队列 datum 中

    :param Queue urls: 需要请求的 url 所在的队列
    :param Queue datum: 写入响应的队列

    示例

    >>> urls = multiprocessing.Queue()
    >>> responses = multiprocessing.Queue()
    >>> shd1 = SimpleHttpDownloader(urls, responses)
    >>> shd2 = SimpleHttpDownloader(urls, responses)
    >>> shd3 = SimpleHttpDownloader(urls, responses)
    >>> for i in [shd1, shd2, shd3]:
    ...     i.start()
    >>> for i in [shd3, shd2, shd1]:
    ...     i.join()
    >>> while not responses.empty():
    ...     data = responses.get()
    ...     assert isinstance(data, bytes)
    """
    session: r.Session
    urls: Queue
    datum: Queue

    def __init__(self, urls: Queue, datum: Queue):
        super().__init__()
        self.urls = urls
        self.datum = datum
        self.session = r.Session()

    def config(self, session):
        self.session = session

    def run(self):
        while True:
            try:
                url = self.urls.get(timeout=10)
            except TimeoutError:
                break
            except Empty:
                break

            true_url = self.before_download(url)

            try:
                resp = self.session.get(true_url, timeout=300)
                content = resp.content
            except r.RequestException as e:
                message = f"{true_url} error: {e.args[0]}"
                content = message.encode()

            true_content = self.after_download(content)

            self.datum.put(true_content, timeout=1)

    def before_download(self, url):
        """在下载前对 url 的处理，默认原样返回

        可重载
        """
        return url

    def after_download(self, data: bytes):
        """下载后对资源的处理，默认原样返回下载到的资源

        可重载
        """
        return data
